﻿#include "akrypt2.h"
#include "password.h"
#include <qfile.h>
#include <qmessagebox.h>
#include "addkey.h"
#include "ui_password.h"



MainWindow::MainWindow(QWidget *parent, Qt::WFlags flags)
	: QMainWindow(parent, flags), _dbase(NULL), _ed(NULL), _hd(NULL),  _ikd(NULL),_logFile("logfile.txt")
{
	if (!_logFile.open( QIODevice::WriteOnly | QIODevice::Append )) 
		showError(this, tr("Не удалось открыть лог"));
	else
		_logStream = new QTextStream(&_logFile);
		
	ui.setupUi(this);
	ui.tabBase->setLayout(ui.verticalLayout);
	ui.tabCiphers->setLayout(ui.verticalLayout_3);
	setLayout(ui.verticalLayout_2);

	connect(ui.actionOpenBase, SIGNAL(activated()), this, SLOT(openBase()));
	connect(ui.actionCloseBase, SIGNAL(activated()), this, SLOT(closeBase()));
	connect(ui.actionEncrypt, SIGNAL(activated()), this, SLOT(actionEncrypt()));
	connect(ui.actionHash, SIGNAL(activated()), this, SLOT(actionHash()));
	connect(ui.actionHashCheck, SIGNAL(activated()), this, SLOT(actionHashCheck()));
	connect(ui.tableBase, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(showBaseTableContextMenu(const QPoint &)));
	connect(ui.tableCiphers, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(showCypherTableContextMenu(const QPoint &)));
	connect(ui.actionAddKey, SIGNAL(activated()), this, SLOT(actionAddKey())); 
	connect(ui.actionImport, SIGNAL(activated()), this, SLOT(actionImport())); 

	loadCiphers();

}

void MainWindow::showBaseTableContextMenu(const QPoint &pos) {
	QMenu *menu = new QMenu;
	QAction* add = menu->addAction(tr("Добавить ключ"), this, SLOT(actionAddKey()));
	QAction* del = menu->addAction(tr("Удалить ключ"), this, SLOT(deleteKey()));
	QAction* useKey = menu->addAction(tr("Зашифровать с этим ключом"), this, SLOT(encryptWithKey()));
	QAction* exp = menu->addAction(tr("Экспортировать ключ"), this, SLOT(exportKey()));
	if (ui.tableBase->selectedItems().count() == 0) {
		useKey->setDisabled(true);
		del->setDisabled(true);
		exp->setDisabled(true);
	}
	if (_dbase == NULL) {
		add->setDisabled(true);
		del->setDisabled(true);
		exp->setDisabled(true);
		
	}
	menu->exec(ui.tableBase->mapToGlobal(pos));
}

void MainWindow::exportKey() {
	ak_master< ak_symmetric_key > key = NULL;
	QString keyID = ui.tableBase->selectedItems()[0]->text();
	QString passwd = "";
	if ((passwd = getPassword(this, tr("Введите пароль доступа к ключу"))).isEmpty())
		return;
    
 // получаем ключ доступа для экспортируемого ключа
   ak_master< ak_block_key> masterkey =
	   ak_gost::new_key_from_password(passwd.toStdString());
   ak_option_block_key ok( masterkey );
   wipeQString(passwd);

   try{
	   key = _dbase->symmetric_key( keyID.toStdString(), ok );
   } catch( ak_error e ) {
       if( e.message().find("CRC")) {
            showError(this, tr("Введен неверный пароль доступа"));
            return;
       }
	   showError(this, tr("Ошибка экспорта ключа: ") + QString::fromStdString(e.what()));
       return;
    }
	QFileDialog::Options options;
	QString selectedFilter;
	QString fileName = QFileDialog::getSaveFileName(this,
                                 tr("Добавить файлы"),
								 "","",&selectedFilter,
                                 options);
	if (fileName.isEmpty())
		return;

	QString passwd2 = "";
	if ((passwd2 = getPassword(this, tr("Введите пароль доступа для файла"))).isEmpty()) 
		return;
    

	// получаем ключ доступа для экспортируемого ключа
	ak_master< ak_block_key> masterkey2 =
		ak_gost::new_key_from_password( passwd2.toStdString() );
	ak_option_block_key ok2( masterkey2 );
	wipeQString(passwd2);

	// только теперь экспорт
	try {
		key->save( fileName.toStdString(), ok2 );
	} catch( ak_error e ) {
		showError(this, tr("При экспорте ключа произошла ошибка: ") + QString::fromStdString(e.what()));
		return;
	}
	showInfo(this, tr("Экспорт завершен!"));

}

void MainWindow::showCypherTableContextMenu(const QPoint &pos) {
	QMenu *menu = new QMenu;
	QAction* useCypher = menu->addAction(tr("Создать ключ для этого алгоритма"), this, SLOT(addKeyForCypher()));
	if (ui.tableCiphers->selectedItems().count() == 0)
		useCypher->setDisabled(true);
	if (_dbase == NULL)
		useCypher->setDisabled(true);
	menu->exec(ui.tableCiphers->mapToGlobal(pos));
}

void MainWindow::returnPassword(QString& pass) {
	_pass = pass;
	wipeQString(pass);
}
//! Если функция вернула пустую строку -
//! пользователь нажал "отмена"
QString MainWindow::getPassword(QWidget* parent, QString msg) {
	//QString msgFirst = tr("Введите пароль доступа");
	//QString msgSecond = tr("Повторите пароль");
	_pass = "";
	if (parent = NULL)
		parent = this;
	PasswordDialog pd(this);
	connect(&pd, SIGNAL(returnPassword(QString & )), this, SLOT(returnPassword(QString &)));
	/*if (second)
		pd.setText(msgSecond);
	else
		pd.setText(msgFirst);*/
	pd.setText(msg);
	while (true) {
		if (pd.exec() == QDialog::Rejected)
			return "";
		if (_pass.isEmpty()) 
			showError(this, ("Введите непустой пароль!"));
		else
			return _pass;
	}
}

void  MainWindow::wipeQString( QString& str, ak_rng *rng )
{
  for( ak_uint32 idx = 0; idx < str.size(); idx++ ) {
    ak_uint8 ch = rng->uint8();
     if( ch < 10 ) ch += 30;
    str[idx] = ch;
  }
  for( ak_uint32 i = 0; i < rng->uint32()%77; i++ ) rng->uint32();
}

void MainWindow::actionAddKey(const QString& cypher) {
	_ak = new AddKeyDialog(this, _dbase, cypher);
	_ak->exec();
	reloadBase();
}

void MainWindow::addKeyForCypher() {
	actionAddKey(ui.tableCiphers->selectedItems()[0]->text());
}

void MainWindow::deleteKey() {
	if (QMessageBox::question(this, tr("Удаление ключа"), tr("Вы действительно хотите удалить ключ из базы?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
		QString id = ui.tableBase->selectedItems()[0]->text();
		_dbase->remove(id.toStdString());
		reloadBase();
	}
}

bool MainWindow::openBase() {
	QFileDialog::Options options;
	QFileDialog dialog;
    QString selectedFilter;
	options |= QFileDialog::ShowDirsOnly;
	QString fileName = QFileDialog::getExistingDirectory(this,
                                 tr("Открыть базу"),
                                 "",
                                 options);
     if (!fileName.isEmpty()) {
        loadBase(fileName);
		return true;
	 }
	 return false;

}

void MainWindow::closeBase() {
	loadBase(QString(""));
	ui.actionAddKey->setEnabled(false);
	ui.actionImport->setEnabled(false);
}

void MainWindow::encryptWithKey() {
	actionEncrypt(ui.tableBase->selectedItems()[0]->text());
}

void MainWindow::actionEncrypt(const QString& id) {
	if (_ed == NULL)
		_ed = new EncryptDialog(this, id);
	_ed->exec();
	delete _ed;
	_ed = NULL;
}

void  MainWindow::actionHash(){
	if (_hd == NULL)
		_hd = new HashDialog(this);
	_hd->exec();
	delete _hd;
	_hd = NULL;
}

void  MainWindow::actionImport(){
	if (_ikd == NULL)
		_ikd = new ImportDialog(this);
	if (_ikd->exec()==QDialog::Accepted)
		reloadBase();
	delete _ikd;
	_ikd = NULL;
}

void  MainWindow::actionHashCheck(){
	QFileDialog::Options options;
	QString selectedFilter;
	QString fileName = QFileDialog::getOpenFileName(this,
                                 tr("Выберите файл для проверки"),
								 "","",&selectedFilter,
                                 options);
	if (fileName.isEmpty())
		return;
	QString sh, sf, sa;
	QFile f(fileName);
	QMap<QString, HashLine *> hashes;
	QStringList filesList;
	f.open(QIODevice::ReadOnly);
	QTextStream fp(&f);
	while( !fp.atEnd()) {
      fp >> sh >> sf >> sa;
      if( fp.status() == QTextStream::Ok) {		 
		  hashes[sf] = new HashLine(sh, sa);
		  filesList.append(sf);
	  }
	}

	_hct = new HCThread(this, filesList, hashes);

	QString title = tr("Проверка целостности");

	_pd = new ProgressDialog(this, filesList.count(), filesList[0], title, false);

	connect(_hct, SIGNAL(jobFinished(QString )), _pd, SLOT(stepProgress(QString )), Qt::QueuedConnection);
	connect(_pd, SIGNAL(rejected()), _hct, SLOT(cancel()), Qt::QueuedConnection);
	connect(_hct, SIGNAL(jobAborted()), _pd, SLOT(accept()), Qt::QueuedConnection);
	connect(_hct, SIGNAL(threadFinished()), this, SLOT(threadFinished()), Qt::QueuedConnection);

	_pd->show();
	_hct->start();
}

void MainWindow::threadFinished() {
	_hct->wait();
	showResults(_hct->_out);
	delete _hct;
}

void MainWindow::showInfo(QWidget *parent, QString msg) {
	QMessageBox::information(parent, tr("Информация"), msg, QMessageBox::Ok);
}

void MainWindow::showError(QWidget *parent, QString msg) {
	QMessageBox::critical(this, tr("Ошибка!"), msg, QMessageBox::Ok);
}

void MainWindow::loadCiphers(QComboBox* comboBox) {
	for(unsigned int j = 0; j < ak_oids_count(); j++) {
	    ak_oid oid=ak_oids(j);
		if( (oid.type() == ak_oid::block_cipher || oid.type() == ak_oid::stream_cipher) && (oid.mode() == ak_oid::undefined))
			comboBox->addItem(QString::fromStdString(oid.name()));
      }
}

void MainWindow::loadHashes(QComboBox* comboBox) {
	for(unsigned int j = 0; j < ak_oids_count(); j++) {
	    ak_oid oid=ak_oids(j);
		if( (
			//oid.type() == ak_oid::hmac_function	|| пока не реализовано ключевое хэширование
			oid.type() == ak_oid::hash_function) && (oid.mode() == ak_oid::undefined))
			comboBox->addItem(QString::fromStdString(oid.name()));
      }
}

void MainWindow::loadCiphers() {
	ui.tableCiphers->setRowCount(0);
	unsigned int j = 0;
	unsigned int i = 0;
	
	for( j = 0; j < ak_oids_count(); j++) {
		
	    ak_oid oid=ak_oids(j);

		if( oid.mode() == ak_oid::wcurve_set_params  || oid.type() == ak_oid::identifier)
			continue;
		
		QTableWidgetItem *item1=new QTableWidgetItem (QString::fromStdString(oid.name()));
		QTableWidgetItem *item2;
		QTableWidgetItem *item3;

		if( oid.type() == ak_oid::block_cipher )
			item2=new QTableWidgetItem (tr("блочный шифр"));
		if( oid.type() == ak_oid::stream_cipher )
			item2=new QTableWidgetItem (tr("поточный шифр"));
		if( oid.type() == ak_oid::hybrid_cipher )
			item2=new QTableWidgetItem (tr("гибридная схема шифрования")); 
		if( oid.type() == ak_oid::hash_function ) 
			item2=new QTableWidgetItem (tr("функция хеширования")); 
		if( oid.type() == ak_oid::hmac_function )
			item2=new QTableWidgetItem(tr("ключевая функция хеширования"));
		if( oid.type() == ak_oid::digital_signature )
			item2=new QTableWidgetItem (tr("цифровая подпись"));
		if( oid.type() == ak_oid::random_generator )
			item2=new QTableWidgetItem (tr("генератор случайных чисел"));  
		if( oid.type() == ak_oid::identifier )
			item2=new QTableWidgetItem (tr("идентификатор"));
		
		if( oid.mode() == ak_oid::parameter )
			item3=new QTableWidgetItem (tr("данные"));
		/*if( oid.mode() == ak_oid::wcurve_set_params )
			item3=new QTableWidgetItem (tr("параметры эллиптической кривой"));*/
		if( oid.mode() == ak_oid::kbox ) 
			item3=new QTableWidgetItem (tr("перестановки"));
		if( oid.mode() == ak_oid::ecb )
			item3=new QTableWidgetItem (tr("режим простой замены"));
		if( oid.mode() == ak_oid::cfb ) 
			item3=new QTableWidgetItem (tr("режим гаммирования с обратной связью"));
		if( oid.mode() == ak_oid::cbc ) 
			item3=new QTableWidgetItem (tr("режим шифрования CBC"));
		if( oid.mode() == ak_oid::xcrypt ) 
			item3=new QTableWidgetItem (tr("режим гаммирования"));
		if( oid.mode() == ak_oid::a8 )
			item3=new QTableWidgetItem (tr("режим гаммирования по модулю 2^8"));
		if( oid.mode() == ak_oid::a32 ) 
			item3=new QTableWidgetItem (tr("режим гаммирования по модулю 2^32"));
		if( oid.mode() == ak_oid::undefined)
			item3=new QTableWidgetItem (tr("режим неопределен"));
		
		ui.tableCiphers->insertRow(i);
		ui.tableCiphers->setItem(i,0,item1);
		ui.tableCiphers->setItem(i,1,item2);
		ui.tableCiphers->setItem(i,2,item3);
		i++;
	
      }
}

void MainWindow::loadKeys(QTableWidget& lilst) {
	unsigned int j = 0;
	for( ak_keydb::const_iterator i = _dbase->begin(); i != _dbase->end(); i++, j++ ) {
        std::string keyID = (*i).first, oid, ds;
        _dbase->info( keyID, oid, ds );
		QTableWidgetItem *item1=new QTableWidgetItem(QString::fromStdString(keyID));
		QTableWidgetItem *item2=new QTableWidgetItem (QString::fromStdString(ak_oids_by_oid(oid).name()));
		QTableWidgetItem *item3=new QTableWidgetItem (QString::fromStdString(ds.data()));
		ui.tableBase->insertRow(j);
		ui.tableBase->setItem(j,0,item1);
		ui.tableBase->setItem(j,1,item2);
		ui.tableBase->setItem(j,2,item3);
      }
}

void MainWindow::loadKeys(QComboBox& comboBox, QStringList& list) {
	for( ak_keydb::const_iterator i = _dbase->begin(); i != _dbase->end(); i++) {
        std::string keyID = (*i).first, oid, ds;
        _dbase->info( keyID, oid, ds );
		QString s = QString::fromUtf8(keyID.data(), keyID.size()) + " / " + 
												QString::fromStdString(ak_oids_by_oid(oid).name()) + " / " + 
												QString::fromStdString(ds.data());
		comboBox.addItem(s);
		list.push_back(QString::fromUtf8(keyID.data(), keyID.size()));
	}
}

void MainWindow::reloadBase() {
	if (_dbase != NULL) {
		QString path = QString::fromStdString(_dbase->path());
		loadBase(path);
	}
}

void MainWindow::loadBase(const QString& path) {
	if (_dbase != NULL)
		delete _dbase;
	ui.tableBase->setRowCount(0);
	if (path.isEmpty()) {
		_dbase = NULL;
		return;
	}
	_dbase = new ak_keydb(path.toStdString());
	loadKeys(*(ui.tableBase));
	ui.actionAddKey->setEnabled(true);
	ui.actionImport->setEnabled(true);
}
void MainWindow::showResults(QList<CheckResult> res){
	QList<CheckResult>::const_iterator i;
	_rd = new ResultDialog(this);
	int j = 0;
	for(i = res.begin(); i != res.end(); i++, j++) {
		QTableWidgetItem *item1 = new QTableWidgetItem(i->filename());
		QTableWidgetItem *item2 = new QTableWidgetItem(i->status() ? "Ok" : "No");
		_rd->ui->tableResult->insertRow(j);
		_rd->ui->tableResult->setItem(j,0,item1);
		_rd->ui->tableResult->setItem(j,1,item2);	
	}
	_rd->ui->tableResult->resizeColumnsToContents();
	_rd->exec();
}


MainWindow::~MainWindow()
{
	_logFile.close();
}
